#include <mathematics/Sphere.h>
#include <mathematics/Plane.h>
#include <mathematics/AABB.h>

namespace ne
{
    Sphere::Sphere()
        : center(Vector3::ZERO), radius(0.0f)
    {
    }

    Sphere::Sphere(const Sphere &s)
        : center(s.center), radius(s.radius)
    {
    }

    Sphere::Sphere(const Vector3 &vCenter, const real r)
        : center(vCenter), radius(r)
    {
    }

    void Sphere::redefine(const Vector3 &vCenter, const real r)
    {
        center = vCenter;
        radius = r;
    }

    bool Sphere::intersects(const Sphere &s) const
    {
        if (center.squaredDistance(s.center) <= radius * radius + s.radius * s.radius)
        {
            return true;
        }
        return false;
    }

    bool Sphere::intersects(const AABB &a) const
    {
        if (a.isNull()) return false;
        if (a.isInfinite()) return true;

        // Sphere's center in the AABB
        if (a.intersects(center))
        {
            return true;
        }

        // AABB's center in the sphere
        Vector3 aCenter = a.getCenter();
        if (intersects(aCenter))
        {
            return true;
        }

        Vector3 dist = center - aCenter;
        real d = dist.length() - radius;
        Vector3 point = d * dist.getUnit();

        return a.intersects(point);
    }

    bool Sphere::intersects(const Plane &p) const
    {
        real d = p.getDistance(center);
        if (d <= radius)
        {
            return true;
        }
        return false;
    }

    bool Sphere::intersects(const Vector3 &v) const
    {
        if (center.squaredDistance(v) <= radius * radius)
        {
            return true;
        }
        return false;
    }

    Sphere& Sphere::operator = (const Sphere &s)
    {
        center = s.center;
        radius = s.radius;
        return *this;
    }

    bool Sphere::operator == (const Sphere &s) const
    {
        return (center == s.center) && (radius == s.radius);
    }

    bool Sphere::operator != (const Sphere &s) const
    {
        return (center != s.center) || (radius != s.radius);
    }

    std::ostream& operator << (std::ostream &o, const Sphere &s)
    {
        o << "Sphere(center=" << s.center <<
                  ", radius=" << s.radius << ")";
        return o;
    }
}
